package com.sky.templatehttpd;

import java.io.IOException;
import java.net.Socket;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Handles one query, parses the HTTP request and returns the response.
 * Private class, don't use it (Internally used by Server).
 */
class Session implements Runnable {

    /** Commons logger used for logging. */
    private static final Log LOG = LogFactory.getLog(Session.class);
    /** Opened and connected socket containing HTTP request. */
    private final Socket socketRequest;
    /** Parent server, fill the response content. */
    private final Server server;

    /**
     * Create a new Session handler.
     * @param server Opened and connected socket containing HTTP request.
     * @param socket Parent server, fill the response content.
     */
    Session(Server server, Socket socketRequest) {
        this.socketRequest = socketRequest;
        this.server = server;
    }

    /**
     * Create a thread to handle request read, split and response send.<br/>
     * This thread exits when the response is sent.
     */
    public void process() {
        Thread thread = new Thread(this);
        thread.setDaemon(true);
        thread.start();
    }

    /**
     * Handle one request split and response send.
     * Request is read from socket.
     * Response is query to server with (abstract) method serve.
     * @throws NullPointerException on null socket
     */
    @Override
    public void run() {
        if (socketRequest == null) {
            throw new NullPointerException("Invalid request socket.");
        }

        try {
            // Get request from client stream.
            Request request;
            try {
                request = new Request(socketRequest.getInputStream());
            } catch (Exception ex) {
                sendErrorResponse(Response.HTTP_BADREQUEST, ex.getMessage());
                return;
            }
            // Create and send response.
            Response response;
            try {
                response = server.serve(request.uri(), request.method(), request.header(), request.params());
            } catch (Exception ex) {
                sendErrorResponse(Response.HTTP_INTERNALERROR, ex.getMessage());
                return;
            }
            sendResponse(response);

        } finally {
            try {
                socketRequest.close();
            } catch (IOException ioex) {
                LOG.info("Ignored Exception while closing request socket", ioex);
            }
        }
    }

    /**
     * Returns an error message as a HTTP response and
     * throws InterruptedException to stop further request processing.
     */
    private void sendErrorResponse(String status, String msg) {
        sendResponse(new Response(status, Response.MIME_PLAINTEXT, msg));
    }

    /**
     * Sends given response to the socket.
     */
    private void sendResponse(Response response) {
        if (response == null) {
            sendErrorResponse(Response.HTTP_INTERNALERROR, "Invalid null response to send.");
            return;
        }
        try {
            response.send(socketRequest.getOutputStream());
        } catch (IOException ioex) {
            LOG.info("Response send fail, " + ioex.getMessage() + ".");
        }
    }
}
